/**
 * Util Functions
 */

import _ from 'lodash'
// import Loading from './loading'
import SparkMD5 from 'spark-md5'
import Tip from './tip'

export default {
  /**
   * Sleep Timeout
   * @param  {Number}   ms Sleep timeout
   * @return {Promise}      SetTimeout promise
   */
  sleep(ms = 500) {
    if (typeof ms !== 'number') {
      throw new Error('param must be a number')
    }
    return new Promise(res => {
      setTimeout(res, ms)
    })
  },

  /**
   * 遍历一棵树，对所有结点做处理，子节点属性为children
   * @param {Object} tree
   * @param {Function} fn
   */
  travTree(tree, fn = function() {}, childrenName = 'children') {
    function trav(node, parent) {
      fn(node, parent)
      if (node[childrenName] && node[childrenName].length > 0) {
        for (let item of node[childrenName]) {
          trav(item, node)
        }
      }
    }

    if (_.isArray(tree)) {
      for (let item of tree) {
        trav(item, null)
      }
    } else {
      trav(tree, null)
    }
  },

  /**
   * 去除首尾空格或内部全部空格
   * @param {String} string
   * @param {Boolean} isRemoveAllSpace
   */
  trim(string, isRemoveAllSpace = false) {
    let reg = /(^\s*)|(\s*$)/g
    if (isRemoveAllSpace) {
      reg = /\s/g
    }
    return string.replace(reg, '')
  },

  /**
   * 滚动到第一个错误点
   * 仅限用于element表单错误
   */
  scrollToFirstError(queryList = [], top = 50) {
    let $errors = [...$('.el-form').find('.is-error')]
    for (let item of queryList) {
      $errors = [...$errors, ...$(item)]
    }
    if ($errors.length > 0) {
      let min = Infinity
      $errors.forEach(function(o) {
        if (min > $(o).offset().top) {
          min = $(o).offset().top
        }
      })
      $('html, body').animate({
        scrollTop: min - top
      })
      return true
    }
    return false
  },

  /**
   * 滚动到页面指定位置
   * @param {Number} pos
   * @param {Boolean} isForceScrollUp 是否强制约定只往上回滚
   */
  scrollTo(pos, isForceScrollUp = false) {
    if (isForceScrollUp && window.scrollY < pos) return
    $('html, body').animate({
      scrollTop: pos
    })
  },

  /**
   * 为数字字符串加上三位一个的逗号
   * @param {String} str
   */
  addNumberCommas(str) {
    str = str.toString()
    let minus = false
    if (/^-/.test(str)) {
      minus = true
      str = str.replace(/^-/, '')
    }
    let [int, float] = str.split('.')
    let result = []
    for (let i = 0; i < int.length; i++) {
      result = [int[int.length - i - 1], ...result]
      if (i % 3 == 2 && i != int.length - 1) {
        result = [',', ...result]
      }
    }
    if (float) {
      return `${minus ? '-' : ''}${result.join('')}.${float}`
    }
    return `${minus ? '-' : ''}${result.join('')}`
  },

  /**
   * 限制输入校验
   * @param {Number} i 位整数
   * @param {Number} f 位小数
   * @param {String} value 待校验的值
   * @param {String} target 待校验的名称
   * @param {Boolean} isMoreThenZero 是否大于0
   */
  float(i, f, value, target, isMoreThenZero = true) {
    let reg
    if (f == 0) {
      reg = new RegExp(`^\\d{1,${i}}$`)
    } else {
      reg = new RegExp(`^\\d{1,${i}}(\\.\\d{1,${f}})?$`)
    }
    if (reg.test(value)) {
      if (Number(value) == 0 && isMoreThenZero) {
        return '请输入大于零的数值'
      }
      return ''
    }
    if (value == '' || value == undefined) {
      return `${target}不能为空`
    }
    let errmsg = `最多包含${i}位整数`
    if (f != 0) {
      errmsg += `和${f}位小数`
    }
    return errmsg
  },

  /**
   * 根据属性值查找key
   * @param {Object} obj
   * @param {String} value
   * @param {Function} compare
   */
  findKey(obj, value, compare = (a, b) => a === b) {
    return Object.keys(obj).find(k => compare(obj[k], value))
  },

  /**
   * 浮点相加
   * @param {string} num1 数值1
   * @param {string} num2 数值2
   * @param {Number} len 要保留的小数位
   */
  floatAdd(num1, num2, len) {
    let temp = Number(num1) + Number(num2)
    return this.filterFloatZero(temp.toFixed(len), len)
  },

  /**
   * 浮点相减
   * @param {string} num1 数值1
   * @param {string} num2 数值2
   * @param {Number} len 要保留的小数位
   */
  floatSub(num1, num2, len) {
    let temp = Number(num1) - Number(num2)
    return this.filterFloatZero(temp.toFixed(len), len)
  },

  /**
   * 过滤浮点数，隐藏末尾为0的小数位
   * @param {string} num 要过滤的数值
   * @param {Number} len 要保留的小数位
   */
  filterFloatZero(num, len) {
    if (num === undefined || num === null) return
    let res = Number(num).toFixed(len)
    let tempStr = res.split('.') // 拆出小数位的数
    if (tempStr.length <= 1) return num
    if (res == parseInt(num, 10)) return parseInt(num, 10)
    let temp = len
    for (let i = 1; temp > 0; temp--, i++) {
      if (Number(tempStr[1].substr(temp - 1, 1)) === 0) {
        // 末尾为0,则砍去末尾
        res = Number(res).toFixed(tempStr[1].length - i)
      } else {
        break
      }
    }
    return res
  },

  /**
   * 点击复制文本
   * @param {string} text 文本值
   */
  copyByClick(text) {
    try {
      let input = document.createElement('input')
      document.body.appendChild(input)
      input.value = text
      input.select()
      document.execCommand('copy') // 执行浏览器复制命令
      Tip.success('已复制当前点击的文本')
      document.body.removeChild(input)
    } catch (e) {
      console.log(e)
      Tip.warn('复制当前点击文本失败')
    }
  },

  /**
   * 检查对象字段是否为空或者null
   * @param {Array} paraArr 数组 需要检查的字段数组
   * @param {Object} obj 对象 需要检查的对象
   */
  checkParaIsNullOrEmpty(paraArr, obj) {
    // 检测对象某个值是否为空
    return paraArr.every(key => {
      return ![undefined, null, ''].includes(obj[key])
    })
  },

  /**
   * 复制对象中指定key值
   * @param {Array} paraArr 数组 需要复制的key值数组
   * @param {Object} obj 对象
   * @param {boolean} bool 值为数组是否取第一个值
   */
  copyObjKeys(paraArr, obj, bool = true) {
    const res = {}
    paraArr.map(item => {
      if (
        Object.prototype.toString.call(obj[item]) == '[object Array]' &&
        obj[item].length > 0 &&
        bool
      ) {
        res[item] = obj[item][0]
      } else {
        res[item] = obj[item]
      }
    })
    return res
  },

  /**
   * 找出两个对象中指定字段相等且值不为空的元素
   * @param {Object} OriginalObj 对象
   * @param {Object} obj 对象
   * @param {Array} fields  字段数组
   */
  compareObj(OriginalObj, obj, fields) {
    const res = []
    fields.map(key => {
      if (![undefined, null, ''].includes(OriginalObj[key])) {
        if (OriginalObj[key] === obj[key]) {
          res.push(key)
        }
      }
    })
    return res
  },

  isUndef(val) {
    return val === undefined || val === null
  },

  isDef(val) {
    return val !== undefined && val !== null
  },

  /**
   * 把整形转换为四字码
   * @param {Number} int 整形数据
   */
  fromFourCC(int) {
    let v = parseInt(int)
    return escape(
      String.fromCharCode(
        v >>> 24,
        (v >>> 16) & 0xff,
        (v >>> 8) & 0xff,
        v & 0xff
      ).toString()
    ).replace(/%00/g, '')
  },
  /**
   * 把四字码转换成整形
   * @param {String} four 四字码
   */
  makeFourCC(four) {
    return (
      (four.charCodeAt(0) << 24) |
      (four.charCodeAt(1) << 16) |
      (four.charCodeAt(2) << 8) |
      four.charCodeAt(3)
    )
  },
  /**
   * 遍历获取所有的分页列表数据
   * @param {Object} res 第一次列表页返回数据
   * @param {Function} request 请求
   * @param {Object} param 请求参数
   */
  async getAllListData(res, request, param = {}) {
    while (res.pageNo * res.pageSize < res.total) {
      res.pageNo += 1
      let moreRes = await request({
        pageSize: res.pageSize,
        pageNo: res.pageNo,
        ...param
      })
      if (!moreRes.code) {
        res.data.push(...moreRes.data)
      } else {
        break
      }
    }
    return res.data
  },
  /**
   * 转换字节大小展示KB\MB\GB
   * @param {Number} size 大小
   */
  conversionByte(size) {
    let b = size
    let kb = b / 1024
    let mb = kb / 1024
    let gb = mb / 1024
    if (kb < 1) {
      return `${b}B`
    } else if (kb >= 1 && mb < 1) {
      return `${kb.toFixed(2)}KB`
    } else if (mb >= 1 && gb < 1) {
      return `${mb.toFixed(2)}MB`
    } else {
      return `${gb.toFixed(2)}GB`
    }
  },
  /**
   * 计算文件MD5校验值
   * @param {File} file 文件对象
   * @param {Function} callBack 获取完MD5后执行回调
   */
  async getCheckNum(file, callBack = Function()) {
    var blobSlice =
        File.prototype.slice ||
        File.prototype.mozSlice ||
        File.prototype.webkitSlice,
      chunkSize = 2097152, // Read in chunks of 2MB
      chunks = Math.ceil(file.size / chunkSize),
      currentChunk = 0,
      spark = new SparkMD5.ArrayBuffer(),
      fileReader = new FileReader()

    fileReader.onload = async function(e) {
      spark.append(e.target.result) // Append array buffer
      currentChunk++

      if (currentChunk < chunks) {
        loadNext()
      } else {
        await callBack(spark.end())
      }
    }

    fileReader.onerror = function() {
      console.warn('oops, something went wrong.')
    }

    function loadNext() {
      var start = currentChunk * chunkSize,
        end = start + chunkSize >= file.size ? file.size : start + chunkSize

      fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
    }

    loadNext()
  },
  /**
   * 格式化省级行政区文字
   * @param {String} province 省份名字
   */
  formatProvince(province) {
    return province.replace(
      /(省|自治区|壮族自治区|维吾尔自治区|回族自治区|特别行政区|市)/g,
      ''
    )
  },
  /**
   * 计算百分比
   * @param {Number} member 分子
   * @param {Number} denominator 分母
   */
  computedPercent(member, denominator) {
    if (denominator === 0) {
      return 0
    }
    let res = ((member / denominator) * 100).toFixed(2)
    return Number(res) || 0
  },
  /**
   * 是否json字符串
   * @param {Number} str 检测的字符串
   */
  isJSON(str) {
    try {
      if (typeof JSON.parse(str) === 'object') {
        return true
      }
    } catch (e) {
      return false
    }
  },
  /**
   * excel导出（带样式，多sheet）
   * @param {Array<Object>} tables 表格对象
   *          {Object:String} id 表格dom的id
   *          {Object:String} name sheet的名称
   * @param {String} title 导出的excel名称
   * @param {String} styleHtml 表格的css字符串（可内嵌在dom中，则不需要传）
   */
  exportExcel(tables = [], title = '数据', styleHtml = '') {
    const base64 = function(s) {
      return window.btoa(unescape(encodeURIComponent(s)))
    }
    const format = function(s, c) {
      return s.replace(/{(\w+)}/g, function(m, p) {
        return c[p]
      })
    }

    if (tables.length > 0) {
      let uri = 'data:application/vnd.ms-excel;base64,',
        html_start = `<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">`,
        template_ExcelWorksheet = `<x:ExcelWorksheet><x:Name>{SheetName}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions><x:WorksheetSource HRef="sheet{SheetIndex}.htm"/></x:ExcelWorksheet>`,
        template_ListWorksheet = `<o:File HRef="sheet{SheetIndex}.htm"/>`,
        template_HTMLWorksheet = `
------=_NextPart_dummy
Content-Location: sheet{SheetIndex}.htm
Content-Type: text/html; charset=utf-8

          ${html_start}
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
              <link id="Main-File" rel="Main-File" href="../WorkBook.htm">
              <link rel="File-List" href="filelist.xml">
          </head>
          <body><table>{SheetContent}</table></body>
          </html>`,
        template_WorkBook = `MIME-Version: 1.0
X-Document-Type: Workbook
Content-Type: multipart/related; boundary="----=_NextPart_dummy"

------=_NextPart_dummy
Content-Location: WorkBook.htm
Content-Type: text/html; charset=utf-8

          ${html_start}
          <head>
          <meta name="Excel Workbook Frameset">
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
          <link rel="File-List" href="filelist.xml">
          <!--[if gte mso 9]><xml>
           <x:ExcelWorkbook>
              <x:ExcelWorksheets>{ExcelWorksheets}</x:ExcelWorksheets>
              <x:ActiveSheet>0</x:ActiveSheet>
           </x:ExcelWorkbook>
          </xml><![endif]-->
          <style>
          ${styleHtml}
          </style>
          </head>
          <frameset>
              <frame src="sheet0.htm" name="frSheet">
              <noframes><body><p>This page uses frames, but your browser does not support them.</p></body></noframes>
          </frameset>
          </html>
{HTMLWorksheets}
Content-Location: filelist.xml
Content-Type: text/xml; charset="utf-8"

          <xml xmlns:o="urn:schemas-microsoft-com:office:office">
              <o:MainFile HRef="../WorkBook.htm"/>
              {ListWorksheets}
              <o:File HRef="filelist.xml"/>
          </xml>
          ------=_NextPart_dummy--
          `

      let context_WorkBook = {
        ExcelWorksheets: '',
        HTMLWorksheets: '',
        ListWorksheets: ''
      }

      tables.forEach((item, index) => {
        context_WorkBook.ExcelWorksheets += format(template_ExcelWorksheet, {
          SheetIndex: index,
          SheetName: item.name
        })
        context_WorkBook.HTMLWorksheets += format(template_HTMLWorksheet, {
          SheetIndex: index,
          SheetContent: document.getElementById(item.id).innerHTML
        })
        context_WorkBook.ListWorksheets += format(template_ListWorksheet, {
          SheetIndex: index
        })
      })

      let link = document.createElement('a')
      link.style.display = 'none'
      link.href = uri + base64(format(template_WorkBook, context_WorkBook))
      link.download = `${title}.xlsx`
      link.target = '_blank'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    }
  }
}
